import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import java.io.IOException;

/**
 * Created by IntelliJ IDEA.
 * User: Fili
 * Date: Feb 24, 2007
 * Time: 9:24:43 AM
 */
public class TDCanvas extends Canvas implements Runnable
{
    private Image[] img;
    private static int gameState = STATE.INIT;
    private static int counter = 0;
    private static String map =
            "cba0MLKJ" +
                    "d000000I" +
                    "e0ijkl0H" +
                    "fgh00m0G" +
                    "00000n0F" +
                    "tsrqpo0E" +
                    "u000000D" +
                    "vwxyzABC";

    private static Graphics g;

    private int[] creepX;
    private int[] creepY;

    private Level[] levels;
    private Creep[] creeps;
    private Tower[] towers;

    private int cLevel;

    private int[] mainMenuItems = {
            STR.STR_MENU_INSTANTPLAY,
            STR.STR_MENU_PLAY,
            STR.STR_MENU_OPTIONS,
            STR.STR_MENU_HELP,
            STR.STR_MENU_ABOUT,
            STR.STR_MENU_EXIT};
    private int selectedMenuItem = 0;

    private int fontImg = 0;

    public TDCanvas()
    {
        Thread tdThread;
        tdThread = new Thread(this);
        tdThread.start();
        setFullScreenMode(true);
    }

    protected void paint(Graphics graphics)
    {
        g = graphics;
        updateState();
    }

    public void run()
    {
        while (gameState != STATE.EXIT)
        {
            repaint();
            serviceRepaints();

            try
            {
                Thread.sleep(100);
            }
            catch (InterruptedException e)
            {
                processException(e);
            }
        }
    }


    protected void showNotify()
    {
        System.out.println("showNotify");
    }


    protected void hideNotify()
    {
        System.out.println("hideNotify");
    }

    public static void processException(Exception e)
    {
        e.printStackTrace();
    }


    private void updateState()
    {
        System.out.println("State = " + gameState);
        counter++;

        switch (gameState)
        {
            case STATE.INIT:
                g.setColor(0x000000);
                g.fillRect(0, 0, DEF.SCREEN_WIDTH, DEF.SCREEN_HEIGHT);
                g.setColor(0xffffff);
                g.drawString("Loading", DEF.SCREEN_WIDTH / 2, DEF.SCREEN_HEIGHT / 2, Graphics.HCENTER | Graphics.TOP);
                gameState = STATE.LOADING;
                break;

            case STATE.LOADING:
                loadAll();
                gameState = STATE.INITLEVEL;
                break;

            case STATE.INITLEVEL:
                initLevel();
                gameState = STATE.GAME;

            case STATE.GAME:
                updateCreeps();
                updateTowers();

                drawMap();
                drawCreeps();
                drawTowers();
                drawProjectiles();

                g.setClip(0, 0, DEF.SCREEN_WIDTH, DEF.SCREEN_HEIGHT);
                drawMenu(mainMenuItems);
                break;
        }
    }

    public void loadAll()
    {
        STR.initStrings();
        img = new Image[DEF.IMG_LAST];
        int[][] redPal = {{}, {}};
        int[][] bluePal = {{0xff000000}, {0xff0099ff}};
        int[][] whitePal = {{0xffff0000}, {0xffffffff}};
        try
        {
//            img[DEF.IMG_LOGO] = Image.createImage("/logo.png");
            Image tmp = Image.createImage("/font.png");
            img[DEF.IMG_REDFONT] = switchPal(tmp, redPal);
            img[DEF.IMG_WHITEFONT] = switchPal(tmp, whitePal);
            img[DEF.IMG_BLUEFONT] = switchPal(tmp, bluePal);
            img[DEF.IMG_GRASS] = Image.createImage("/grass.png");
            img[DEF.IMG_ROAD] = Image.createImage("/road.png");
            img[DEF.IMG_CANON1] = Image.createImage("/canon1.png");
            img[DEF.IMG_CANON2] = Image.createImage("/canon2.png");
            img[DEF.IMG_CANON3] = Image.createImage("/canon3.png");
            img[DEF.IMG_CREEP1] = Image.createImage("/creep1.png");
            img[DEF.IMG_CREEP2] = Image.createImage("/creep2.png");
            img[DEF.IMG_BOMB1] = Image.createImage("/bomb1.png");
        }
        catch (IOException e)
        {
            processException(e);
        }

        int n = 0;
        for (int i = 0; i < map.length(); i++)
            if (map.charAt(i) != '0')
                n++;

        towers = new Tower[n];
        for (int i = 0; i < towers.length; i++)
            towers[i] = new Tower();

        creepX = new int[n + 4];
        creepY = new int[n + 4];

        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
            {
                char c = map.charAt(i * 8 + j);
                if (c != '0')
                {
                    if (c <= 'Z')
                        n = 26 + (c - 'A');
                    else
                        n = c - 'a';
                    creepX[n + 2] = j;
                    creepY[n + 2] = i;
                }
            }

        n = creepX.length - 4;

        creepX[0] = creepX[2];
        creepY[0] = creepY[2] - 2;

        creepX[1] = creepX[2];
        creepY[1] = creepY[2] - 1;

        creepX[n + 2] = creepX[n + 1];
        creepY[n + 2] = creepY[n + 1] - 1;

        creepX[n + 3] = creepX[n + 1];
        creepY[n + 3] = creepY[n + 1] - 2;

        creeps = new Creep[DEF.MAX_CREEPS];
        for (int i = 0; i < creeps.length; i++)
            creeps[i] = new Creep();

        levels = new Level[]{
                new Level(DEF.IMG_CREEP1, 10, 1, 3, 50, 1, "Sheepz"),
                new Level(DEF.IMG_CREEP2, 10, 2, 4, 50, 1, "Men in iron"),
                new Level(DEF.IMG_CREEP1, 12, 3, 5, 25, 2, "Sheepz on drugz"),
        };

        cLevel = 2;

        towers[0].x = 1 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;
        towers[0].y = 4 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;
        towers[0].type = DEF.IMG_CANON1;
        towers[0].state = Tower.SEEKING;

        towers[1].x = 6 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;
        towers[1].y = 3 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;
        towers[1].type = DEF.IMG_CANON2;
//        towers[1].state = Tower.SEEKING;

    }

    public int getCreepX(int p)
    {
        p += 2 * DEF.MAP_SPEED;
        if (p < DEF.MAP_SPEED)
            return 0;
        int div = p / DEF.MAP_SPEED;
        int mod = p % DEF.MAP_SPEED;
        int p1, p2;

        p1 = creepX[div - 1];
        p2 = creepX[div];

        p1 = p1 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;
        p2 = p2 * DEF.MAP_CELL_X + DEF.MAP_CELL_X / 2;

        return p1 + ((p2 - p1) * mod) / DEF.MAP_SPEED;
    }

    public int getCreepY(int p)
    {
        p += 2 * DEF.MAP_SPEED;
        if (p < DEF.MAP_SPEED)
            return 0;
        int div = p / DEF.MAP_SPEED;
        int mod = p % DEF.MAP_SPEED;
        int p1, p2;

        p1 = creepY[div - 1];
        p2 = creepY[div];

        p1 = p1 * DEF.MAP_CELL_Y + DEF.MAP_CELL_Y / 2;
        p2 = p2 * DEF.MAP_CELL_Y + DEF.MAP_CELL_Y / 2;

        return p1 + ((p2 - p1) * mod) / DEF.MAP_SPEED;

    }

    public void setMapClip()
    {
        g.setClip(DEF.MAP_OFFSET_X, DEF.MAP_OFFSET_Y, DEF.MAP_CELL_X * 8, DEF.MAP_CELL_Y * 8);
    }

    public void drawMap()
    {
        Image tempImg;
        int p = 0;

        setMapClip();
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
            {
                if (map.charAt(p) == '0')
                    tempImg = img[DEF.IMG_GRASS];
                else
                    tempImg = img[DEF.IMG_ROAD];
                g.drawImage(tempImg, DEF.MAP_CELL_X * j + DEF.MAP_OFFSET_X, DEF.MAP_CELL_Y * i + DEF.MAP_OFFSET_Y, Graphics.LEFT | Graphics.TOP);
                p++;
            }
    }

    public void initLevel()
    {
        for (int i = 0; i < creeps.length; i++)
        {
            if (i < levels[cLevel].creepCount)
            {
                creeps[i].health = levels[cLevel].creepHealth;
                creeps[i].pos = -DEF.MAP_SPEED - i * levels[cLevel].creepEntry;
            }
            else
                creeps[i].health = -1;
        }
    }

    public void drawCreeps()
    {
        for (int i = 0; i < creeps.length; i++)
            if ((creeps[i].health > 0) && (creeps[i].pos > -DEF.MAP_SPEED))
                g.drawImage(img[DEF.IMG_CREEP1], creeps[i].x + DEF.MAP_OFFSET_X, creeps[i].y + DEF.MAP_OFFSET_Y, Graphics.HCENTER | Graphics.VCENTER);
    }

    public void drawTowers()
    {
        for (int i = 0; i < towers.length; i++)
            if (towers[i].state != Tower.UNBUILT)
                g.drawImage(img[towers[i].type], towers[i].x + DEF.MAP_OFFSET_X, towers[i].y + DEF.MAP_OFFSET_Y, Graphics.HCENTER | Graphics.VCENTER);
    }

    public void updateCreeps()
    {
        for (int i = 0; i < creeps.length; i++)
            if (creeps[i].health > 0)
            {
                creeps[i].pos += levels[cLevel].creepSpeed;
                if (creeps[i].pos >= ((creepX.length - 3) * DEF.MAP_SPEED))
                    creeps[i].pos = -DEF.MAP_SPEED;
                creeps[i].x = getCreepX(creeps[i].pos);
                creeps[i].y = getCreepY(creeps[i].pos);
            }
    }

    public int dist(int creep, int tower)
    {
        int dx = towers[tower].x - creeps[creep].x;
        int dy = towers[tower].y - creeps[creep].y;

        return dx * dx + dy * dy;
    }

    public void updateTowers()
    {
        for (int i = 0; i < towers.length; i++)
        {
            if (towers[i].state != Tower.UNBUILT)
            {
                switch (towers[i].state)
                {
                    case(Tower.SEEKING):
                        int min = 1000000;
                        int index = -1;
                        int m;

                        for (int j = 0; j < creeps.length; j++)
                        {
                            if (creeps[j].health > 0)
                            {
                                m = dist(j, i);
                                if (m < 1600)
                                {
                                    if (m < min)
                                    {
                                        min = m;
                                        index = j;
                                    }
                                }
                            }
                        }

                        if (index >= 0)
                        {
                            towers[i].state = Tower.FIRE;
                            towers[i].target = index;
                        }
                        else
                            //wait a little before searching again
                            towers[i].state = Tower.COOLING + 25;
                        System.out.println("Seeked " + counter);
                        break;
                    case(Tower.FIRE):
                        System.out.println("Fireing ball at: " + towers[i].target + " (" + counter + ")");
                        towers[i].state = Tower.COOLING;
                        break;
                }

                if (towers[i].state >= Tower.COOLING)
                {
                    if (towers[i].state - Tower.COOLING > 30)
                    {
                        //if I can still hit my target
                        if ((dist(towers[i].target, i) < 1600) && (creeps[i].health > 0))
                            towers[i].state = Tower.FIRE;
                        else
                            towers[i].state = Tower.SEEKING;
                    }
                    else
                        towers[i].state++;
                }
            }
        }
    }

    public void drawProjectiles()
    {
        for (int i = 0; i < towers.length; i++)
            if (towers[i].state >= Tower.FIRE)
            {
                int dif = towers[i].state - Tower.FIRE;
                System.out.println("dif= " + dif);
                if (dif <= 5)
                {
                    int x1 = creeps[towers[i].target].x + DEF.MAP_OFFSET_X;
                    int y1 = creeps[towers[i].target].y + DEF.MAP_OFFSET_Y;
                    int x2 = towers[i].x + DEF.MAP_OFFSET_X;
                    int y2 = towers[i].y + DEF.MAP_OFFSET_Y;

                    g.setStrokeStyle(Graphics.DOTTED);
                    g.setColor(0xff0000);
                    g.drawLine(x1 + 1, y1, x2 + 1, y2);
                    g.drawLine(x1 - 1, y1, x2 - 1, y2);
                    g.drawLine(x1, y1 + 1, x2, y2 + 1);
                    g.drawLine(x1, y1 - 1, x2, y2 - 1);
//                    g.setColor(0xffaa00);                     
                    g.setColor(255, 255 - dif * 25, 255 - dif * 25);
                    g.drawLine(x1, y1, x2, y2);
                }
            }
    }

    public void drawString(String s, int x, int y, int anchor)
    {
        int lineCount = 1;

        for (int i = 0; i < s.length(); i++)
            if (s.charAt(i) == '\n')
                lineCount++;

        if ((anchor & Graphics.VCENTER) > 0)
            y -= (lineCount * DEF.LINE_HEIGHT) / 2;
        if ((anchor & Graphics.BOTTOM) > 0)
            y -= (lineCount * DEF.LINE_HEIGHT);

        int p = 0;
        int p2 = p;
        int px;
        while (p < s.length())
        {
            while ((p2 < s.length()) && (s.charAt(p2) != '\n'))
                p2++;
            px = x;
            if ((anchor & Graphics.HCENTER) > 0)
                px -= ((p2 - p) * DEF.FONT_WIDTH) / 2;
            if ((anchor & Graphics.RIGHT) > 0)
                px -= ((p2 - p) * DEF.FONT_WIDTH);
            drawString(s, px, y, p, p2);
            y += DEF.LINE_HEIGHT;
            p2++;
            p = p2;
        }
    }


    public void drawString(String s, int x, int y, int p1, int p2)
    {
        int px, py;

        for (int i = p1; i < p2; i++)
        {
            char c = s.charAt(i);

            if ((c >= 'A') && (c <= 'Z'))
            {
                px = (c - 'A') * DEF.FONT_WIDTH;
                py = 0;
            }
            else
                if ((c >= '0') && (c <= '9'))
                {
                    px = (c - '0') * DEF.FONT_WIDTH;
                    py = 13;
                }
                else
                {
                    String tmp = "+-=/.,!?()<> ";
                    int p = tmp.indexOf(c);
                    if (p < 0)
                        p = 7;

                    px = p * DEF.FONT_WIDTH + 11 * DEF.FONT_WIDTH;
                    py = 13;
                }
            g.drawRegion(img[fontImg], px, py, DEF.FONT_WIDTH, DEF.FONT_HEIGHT, Sprite.TRANS_NONE, x + (i - p1) * DEF.FONT_WIDTH, y, Graphics.LEFT | Graphics.TOP);
        }
    }

    public Image switchPal(Image img, int[][] p)
    {
        int[] v = new int[img.getWidth() * img.getHeight()];

        img.getRGB(v, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());

        for (int i = 0; i < v.length; i++)
        {
            for (int j = 0; j < p[0].length; j++)
                if (v[i] == p[0][j])
                    v[i] = p[1][j];
        }

        return Image.createRGBImage(v, img.getWidth(), img.getHeight(), true);
    }

    public static String getString(int p)
    {
        return STR.strings[p];
    }

    public void setFont(int f)
    {
        fontImg = f;
    }

    public void drawMenu(int[] menu)
    {
        int x = DEF.SCREEN_WIDTH / 2;
        int y = (DEF.SCREEN_HEIGHT - menu.length * 16) / 2;

        for (int i = 0; i < menu.length; i++)
            y = drawMenuItem(menu[i], x, y);
    }

    public int drawMenuItem(int m, int x, int y)
    {
        String s;

        if (m == selectedMenuItem)
        {
            setFont(DEF.IMG_REDFONT);
            s = "> " + getString(m) + " <";
        }
        else
        {
            setFont(DEF.IMG_WHITEFONT);
            s = getString(m);
        }
        drawString(s, x, y, Graphics.HCENTER | Graphics.TOP);
        return y + 16;
    }

}
